package com.david.http;

import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.TimeUnit;

import org.apache.commons.collections.CollectionUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.config.SocketConfig;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;

public class HttpClient {

	private PoolingHttpClientConnectionManager connManager;
	private ResponseHandler handler;
	private CloseableHttpClient httpclient;

	private static final int DEFAULT_MAX = 50;
	private static final int DEFAULT_TIME_OUT = 10000;

	public HttpClient() {
		connManager = new PoolingHttpClientConnectionManager();
		connManager.setMaxTotal(DEFAULT_MAX);
		connManager.setDefaultMaxPerRoute(DEFAULT_MAX);
		connManager.setDefaultSocketConfig(SocketConfig.custom()
				.setTcpNoDelay(true).build());
		connManager.closeIdleConnections(30, TimeUnit.SECONDS);

		handler = new ResponseHandler();

		httpclient = HttpClientBuilder.create()
				.addInterceptorFirst(new ResponseInterceptor())
				.setConnectionManager(connManager).build();
	}

	public HttpResult get(String url) throws ClientProtocolException,
			IOException {
		Collection<Header> headers = Collections.emptyList();
		return get(url, headers, DEFAULT_TIME_OUT);
	}

	public HttpResult get(String url, Collection<Header> headers,
			int timeoutMillis) throws ClientProtocolException, IOException {
		HttpGet http = new HttpGet(url);
		setHeaders(http, headers);
		setConfig(http, initConfig(timeoutMillis));
		return execute(http);
	}

	public HttpResult post(String url, Collection<Header> headers,
			HttpEntity entity, int timeoutMillis)
			throws ClientProtocolException, IOException {
		HttpPost http = new HttpPost(url);
		setHeaders(http, headers);
		setConfig(http, initConfig(timeoutMillis));
		setEntity(http, entity);
		return execute(http);
	}

	private void setHeaders(HttpRequestBase http, Collection<Header> headers) {
		if (CollectionUtils.isEmpty(headers)) {
			return;
		}
		http.setHeaders(headers.toArray(new Header[headers.size()]));
	}

	private void setEntity(HttpEntityEnclosingRequest http, HttpEntity entity) {
		http.setEntity(entity);
	}

	private void setConfig(HttpRequestBase http, RequestConfig config) {
		http.setConfig(config);
	}

	private void releaseConnection(HttpRequestBase http) {
		if (http != null) {
			http.releaseConnection();
		}
	}

	private HttpResult execute(HttpRequestBase http)
			throws ClientProtocolException, IOException {
		try {
			return httpclient.execute(http, handler);
		} finally {
			releaseConnection(http);
		}
	}

	private RequestConfig initConfig(int timeOutMillis) {
		return RequestConfig.custom().setSocketTimeout(timeOutMillis)
				.setConnectTimeout(timeOutMillis).setRedirectsEnabled(true)
				.setConnectionRequestTimeout(timeOutMillis)
				.setCircularRedirectsAllowed(true).build();
	}

	public void shutdown() {
		connManager.shutdown();
		try {
			this.httpclient.close();
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

}
